#include <memory.h>
#include "ccon.h"


#define NATIVE_ENDIANITY    'L'


#define STREAM_READ(buf, count)                                             \
        if (stream->read(stream->self, (char*)buf, count) != count)         \
        return CCONRES_END_OF_STREAM

#define SWAP2(v) (((v & 0xff) << 8) | ((v & 0xff00) >> 8)) 
#define SWAP4(v) (((v & 0xff) << 24) | ((v & 0xff00) << 8) |                \
        ((v & 0xff0000) >> 8) | (v >> 24))


/*****************************************************************************
 * INT8
 *****************************************************************************/
cconres_t ccon_ubint8_parse(unsigned char * out, stream_t * stream)
{
	STREAM_READ(out, sizeof(unsigned char));
	return CCONRES_OKAY;
}

cconres_t ccon_sbint8_parse(signed char * out, stream_t * stream)
{
	STREAM_READ(out, sizeof(signed char));
	return CCONRES_OKAY;
}

cconres_t ccon_ulint8_parse(unsigned char * out, stream_t * stream)
{
	STREAM_READ(out, sizeof(unsigned char));
	return CCONRES_OKAY;
}

cconres_t ccon_slint8_parse(signed char * out, stream_t * stream)
{
	STREAM_READ(out, sizeof(signed char));
	return CCONRES_OKAY;
}


/*****************************************************************************
 * INT16
 *****************************************************************************/
cconres_t ccon_ubint16_parse(unsigned short * out, stream_t * stream)
{
	STREAM_READ(out, sizeof(unsigned short));
	#if NATIVE_ENDIANITY == 'L'
	*out = SWAP2(*out);
	#endif
	return CCONRES_OKAY;
}

cconres_t ccon_sbint16_parse(signed short * out, stream_t * stream)
{
	STREAM_READ(out, sizeof(signed short));
	#if NATIVE_ENDIANITY == 'L'
	*out = SWAP2(*out);
	#endif
	return CCONRES_OKAY;
}

cconres_t ccon_ulint16_parse(unsigned short * out, stream_t * stream)
{
	STREAM_READ(out, sizeof(unsigned short));
	#if NATIVE_ENDIANITY == 'B'
	*out = SWAP2(*out);
	#endif
	return CCONRES_OKAY;
}

cconres_t ccon_slint16_parse(signed short * out, stream_t * stream)
{
	STREAM_READ(out, sizeof(signed short));
	#if NATIVE_ENDIANITY == 'B'
	*out = SWAP2(*out);
	#endif
	return CCONRES_OKAY;
}

/*****************************************************************************
 * INT32
 *****************************************************************************/
cconres_t ccon_ubint32_parse(unsigned long * out, stream_t * stream)
{
	STREAM_READ(out, sizeof(unsigned long));
	#if NATIVE_ENDIANITY == 'L'
	*out = SWAP4(*out);
	#endif
	return CCONRES_OKAY;
}

cconres_t ccon_sbint32_parse(signed long * out, stream_t * stream)
{
	STREAM_READ(out, sizeof(signed long));
	#if NATIVE_ENDIANITY == 'L'
	*out = SWAP4(*out);
	#endif
	return CCONRES_OKAY;
}

cconres_t ccon_ulint32_parse(unsigned long * out, stream_t * stream)
{
	STREAM_READ(out, sizeof(unsigned long));
	#if NATIVE_ENDIANITY == 'B'
	*out = SWAP4(*out);
	#endif
	return CCONRES_OKAY;
}

cconres_t ccon_slint32_parse(signed long * out, stream_t * stream)
{
	STREAM_READ(out, sizeof(signed long));
	#if NATIVE_ENDIANITY == 'B'
	*out = SWAP4(*out);
	#endif
	return CCONRES_OKAY;
}


/*****************************************************************************
 * Memory Stream
 *****************************************************************************/
size_t memstream_read(memstream_t * self, char * buf, size_t count)
{
	#define MAX (self->size - self->offset)
	size_t cnt;
	cnt = (count > MAX) ? MAX : count;
	memcpy(buf, &self->buf[self->offset], cnt);
	self->offset += (long)cnt;
	return cnt;
}

size_t memstream_write(memstream_t * self, char * buf, size_t count)
{
	return 0;
}

long memstream_tell(memstream_t * self)
{
	return self->offset;
}

long memstream_seek(memstream_t * self, long offset)
{
	self->offset += offset;
	return self->offset;
}

cconres_t memstream_init(stream_t * stream, memstream_t * self, char * buf, size_t size)
{
	self->buf = buf;
	self->size = size;
	self->offset = 0;
	stream->read = memstream_read;
	stream->write = memstream_write;
	stream->tell = memstream_tell;
	stream->seek = memstream_seek;
	stream->self = self;
	return CCONRES_OKAY;
}


/*****************************************************************************
 * File Stream
 *****************************************************************************/
size_t filestream_read(FILE * self, char * buf, size_t count)
{
	return fread(buf, count, 1, self); 
}

size_t filestream_write(FILE * self, char * buf, size_t count)
{
	return fwrite(buf, count, 1, self);
}

long filestream_tell(FILE * self)
{
	return ftell(self);
}

long filestream_seek(FILE * self, long offset)
{
	fseek(self, (long)offset, SEEK_CUR);
	return ftell(self);
}

cconres_t filestream_init(stream_t * stream, FILE * self)
{
	stream->read = filestream_read;
	stream->write = filestream_write;
	stream->tell = filestream_tell;
	stream->seek = filestream_seek;
	stream->self = self;
	return CCONRES_OKAY;
}











